<?php
/**
 * 自动加载类
 * @author bluelevin(at)gmail.com
 *
 */
class LyAutoloader {
	static private $fileMapping;
	/**
	 * 需要扫描的目录
	 * @var array
	 */
	protected $autoDirs;
	/**
	 * 缓存目录
	 * @var string
	 */
	protected $cacheDir;
	/**
	 * 设置缓存目录
	 * @param $dir string
	 * @return void
	 */
	function setCacheDir($dir){
		$dir = trim($dir,'/\\ ');
		if(!is_dir($dir)){
			@mkdir($dir,0777,true);
		}
		$this->cacheDir = $dir;
	}
	/**
	 * 增加扫描目录,增加后,目录里的所有类都会自动加载
	 * @param $dirs array
	 * @return void
	 */
	function addAutoDirs($dirs){
		if(is_array($dirs)){
			foreach ($dirs as $dir){
				$this->addAutoDirs($dir);
			}
		}else{
			$dirs = trim($dirs,'/\\ ');
			if(is_dir($dirs)){
				$this->autoDirs[] = $dirs;
			}
		}
	}
	/**
	 * 获得指定目录(包括子目录)后缀名为php,inc的所有文件列表
	 * @param string/array $dir
	 * @return array
	 */
	protected function _getFileList($dir){
		static $fileList = array();
		if(is_array($dir)){
			foreach ($dir as $oneDir){
				$this->_getFileList($oneDir);
			}
		}else{
			if(!is_dir($dir)){
				return;
			}
			if ($handle = opendir($dir)) {
				while (false !== ($file = readdir($handle))) {
					if (!in_array($file,array('.','..','.svn'))) {
						$filename = $dir.DIRECTORY_SEPARATOR.$file;
						if (is_dir($filename)) {
							$this->_getFileList($filename);
						} else{
							$ext = pathinfo($file, PATHINFO_EXTENSION);
							if(in_array($ext,array('php','inc'))){
								$fileList[] = $filename;
							}
						}
					}
				}
			}
		}
		return $fileList;
	}
	/**
	 * 以类名为依据生成唯一filename
	 * @param $className
	 * @return string
	 */
	protected  function _buildFilename($className){
		$className = strtolower(trim($className));
		$path = $this->cacheDir.DIRECTORY_SEPARATOR.'__class__cache__';
		if(!is_dir($path)){
			@mkdir($path,0777,true);
		}
		return $path.DIRECTORY_SEPARATOR.$className.'.php';

	}
	/**
	 * 扫描目录,分析目录下的所有文件,把含有PHP类定义的文件全找出来,
	 * 并把信息缓存起来.
	 * @param $force 是否强制扫描
	 * @return void
	 */
	function scanFiles($dev = false){
		$fileMapping = array();
		if(false === $dev && is_dir($this->cacheDir.DIRECTORY_SEPARATOR.'__class__cache__')){
			return;
		}
		$fileList = $this->_getFileList($this->autoDirs);
		if(empty($fileList)){
			return ;
		}
		foreach ($fileList as $filename){
			$content = trim(file_get_contents($filename));
			if(preg_match_all('~^\s*(?:abstract\s+|final\s+)?(?:class|interface)\s+(\w+).*~mi', $content, $classes) > 0){
				foreach($classes[1] as $key => $class){
					$fileMapping[strtolower($class)] = $filename;
					$newFilename = $this->_buildFilename($class);
					$newContent = "<?php include \"".str_replace('\\','/',$filename)."\"; ?>";
					file_put_contents($newFilename,$newContent);
				}
			}
		}
		$mappingFilename = $this->_buildFilename('__fileMapping__');
		$content2 = "<?php \n return ".var_export($fileMapping,true)."\n?>";
		file_put_contents($mappingFilename,$content2);
	}
	/**
	 * 开始autoload,
	 * include采用两次include形式自己加载
	 * filemapping 采用数组记录类名->位置形式自动加载
	 * @return void
	 */
	function beginAutoload($type = 'include'){
		if('include' == $type){
			spl_autoload_register(array($this,'loadClass'));
		}elseif('filemapping' == $type){
			if(isset(self::$fileMapping)){
				self::$fileMapping = include $this->_buildFilename('__fileMapping__');
			}
			spl_autoload_register(array($this,'loadClass2'));
		}
	}
	function loadClass2($className){
		$key = strtolower(trim($className,'./\\ '));
		if(!isset(self::$fileMapping[$key])){
			//@todo 异常
			return ;
		}
		include self::$fileMapping[$key];
	}
	/**
	 * __autoload的代替函数
	 * @param 类名 $className
	 * @return void
	 */
	function loadClass($className){
		$filename = $this->_buildFilename($className);
		if(!is_file($filename)){
			return ;
		}
		include $filename;
	}
}